#include "p2p_server.h"
//#include "rtc_log.h"
//#include "api/create_peerconnection_factory.h"
//#include "api/audio_codecs/audio_decoder_factory.h"
//#include "api/audio_codecs/audio_encoder_factory.h"
//#include "api/video_codecs/video_decoder_factory.h"
//#include "api/video_codecs/video_encoder_factory.h"
//#include "api/audio_codecs/builtin_audio_decoder_factory.h"
//#include "api/audio_codecs/builtin_audio_encoder_factory.h"
//#include "api/video_codecs/builtin_video_decoder_factory.h"
//#include "api/video_codecs/builtin_video_encoder_factory.h"
#include <stdint.h>
#include <functional>
#include <string>
#include "EventLoopMgr.h"


static int s_debug_level = MG_LL_INFO;
#ifdef ANDROID
static const char* s_root_dir = "/sdcard/T30/";
#else
static const char* s_root_dir = "./";
#endif
static const char* s_enable_hexdump = "yes";
static const char* s_ssi_pattern = "#.html";



#if defined(__linux) || defined(__linux__) 
#include <sys/types.h>         
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h> 
#include <netinet/ether.h>   
#include <netinet/ip.h>  
#include <netpacket/packet.h>   
#include <arpa/inet.h>
#include <net/ethernet.h>   
#include <net/route.h>  
#include <netdb.h>
#include <net/if.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/select.h>
#define SOCKET int
#define INVALID_SOCKET  (-1)
#define SOCKET_ERROR    (-1) 

#elif defined(WIN32) || defined(_WIN32) 
#define FD_SETSIZE      1024
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#define SHUT_RD 0
#define SHUT_WR 1 
#define SHUT_RDWR 2

#pragma comment(lib,"iphlpapi.lib")

#else

#endif

std::string P2PServer::GetLocalIPAddress()
{
#if defined(__linux) || defined(__linux__) 
	SOCKET sockfd = 0;
	char buf[512] = { 0 };
	struct ifconf ifconf;
	struct ifreq* ifreq;
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd == INVALID_SOCKET)
	{
		close(sockfd);
		return "0.0.0.0";
	}

	ifconf.ifc_len = 512;
	ifconf.ifc_buf = buf;
	if (ioctl(sockfd, SIOCGIFCONF, &ifconf) < 0)
	{
		close(sockfd);
		return "0.0.0.0";
	}

	close(sockfd);

	ifreq = (struct ifreq*)ifconf.ifc_buf;
	for (int i = (ifconf.ifc_len / sizeof(struct ifreq)); i > 0; i--)
	{
		if (ifreq->ifr_flags == AF_INET)
		{
			if (strcmp(ifreq->ifr_name, "lo") != 0)
			{
				return inet_ntoa(((struct sockaddr_in*)&(ifreq->ifr_addr))->sin_addr);
			}
			ifreq++;
		}
	}
	return "0.0.0.0";
#elif defined(WIN32) || defined(_WIN32)
	PIP_ADAPTER_INFO pIpAdapterInfo = new IP_ADAPTER_INFO();
	unsigned long size = sizeof(IP_ADAPTER_INFO);

	int ret = GetAdaptersInfo(pIpAdapterInfo, &size);
	if (ret == ERROR_BUFFER_OVERFLOW)
	{
		delete pIpAdapterInfo;
		pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[size];
		ret = GetAdaptersInfo(pIpAdapterInfo, &size);
	}

	if (ret != ERROR_SUCCESS)
	{
		delete pIpAdapterInfo;
		return "0.0.0.0";
	}

	while (pIpAdapterInfo)
	{
		IP_ADDR_STRING* pIpAddrString = &(pIpAdapterInfo->IpAddressList);
		while (pIpAddrString)
		{
			if (strcmp(pIpAddrString->IpAddress.String, "127.0.0.1") != 0
				&& strcmp(pIpAddrString->IpAddress.String, "0.0.0.0") != 0)
			{
				// pIpAddrString->IpMask.String 
				//pIpAdapterInfo->GatewayList.IpAddress.String
				std::string ip(pIpAddrString->IpAddress.String);
				//delete pIpAdapterInfo;
				return ip;
			}
			pIpAddrString = pIpAddrString->Next;
		} while (pIpAddrString);
		pIpAdapterInfo = pIpAdapterInfo->Next;
	}

	delete pIpAdapterInfo;
	return "0.0.0.0";
#else
	return "0.0.0.0";
#endif
}


std::list<std::string> P2PServer::m_listLoge;
P2PServer::P2PServer()
{

}

P2PServer::~P2PServer()
{
	Stop();
}

//void P2PServer::SetVideoSource(rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> video_source)
//{
//	video_source_ = video_source;
//}

bool P2PServer::Start(uint16_t port, std::string rootPath, std::string ip)
{
	if (connection_) {
		return false;
	}

	m_root_dir = rootPath;
	if (rootPath.c_str())
	{
		s_root_dir = m_root_dir.c_str();
	}


	EventLoopMngr::GetInstance()->Init(1);

	std::string url = "http://" + ip + ":" + std::to_string(port);

	mg_mgr_init(&mgr_);
	connection_ = mg_http_listen(&mgr_, url.c_str(), P2PServer::OnEvent, this);
	if (!connection_) {
		return false;
	}

	poll_thread_.reset(new std::thread([this] {
		//signaling_thread_ = rtc::Thread::Create();
		//signaling_thread_->Start();

		//peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
		//	nullptr /* network_thread */, nullptr /* worker_thread */,
		//	signaling_thread_.get(), nullptr /* default_adm */,
		//	webrtc::CreateBuiltinAudioEncoderFactory(),
		//	webrtc::CreateBuiltinAudioDecoderFactory(),
		//	webrtc::CreateBuiltinVideoEncoderFactory(),
		//	webrtc::CreateBuiltinVideoDecoderFactory(),
		//	nullptr /* audio_mixer */,
		//	nullptr /* audio_processing */);

		//audio_source_= peer_connection_factory_->CreateAudioSource(cricket::AudioOptions());

		while (connection_) {
			mg_mgr_poll(&mgr_, 30);
		}

		//mutex_.lock();
	/*	p2p_sessios_.clear();*/
		//mutex_.unlock();

		//peer_connection_factory_ = nullptr;
		//signaling_thread_->Stop();
		}));

	if (!m_TaskScheduler)
	{
		m_TaskScheduler = EventLoopMngr::GetInstance()->m_oEventLoop.GetTaskScheduler();
	}

	return true;
}

void P2PServer::Stop()
{
	if (connection_) {
		connection_ = nullptr;
		poll_thread_->join();
		poll_thread_.reset();
		mg_mgr_free(&mgr_);
	}
}

bool P2PServer::IsExistence(uint16_t accountNum)
{
	//std::unique_lock<std::mutex> lock(mutex_);
	std::map<mg_connection*, std::unique_ptr<P2PSession>>::iterator it1 = p2p_sessios_.begin();
	for (; it1 != p2p_sessios_.end(); it1++)
	{
		if (accountNum == it1->second->m_uId)
		{
			return true;
		}
	}
	return false;
}
bool P2PServer::DelAccount(uint16_t accountNum)
{
	bool ret = false;
	//std::unique_lock<std::mutex> lock(mutex_);
	std::map<mg_connection*, std::unique_ptr<P2PSession>>::iterator it1 = p2p_sessios_.begin();
	for (; it1 != p2p_sessios_.end(); it1++)
	{
		if (accountNum == it1->second->m_uId)
		{
			p2p_sessios_.erase(it1);
			ret = true;
			break;
		}
	}
	return ret;
}
int P2PServer::GetListOnLineArray(mg_connection* con, std::vector<uint16_t>& vec)
{
	std::map<mg_connection*, std::unique_ptr<P2PSession>>::iterator it = p2p_sessios_.find(con);
	if (it != p2p_sessios_.end())
	{
		vec.push_back(it->second->m_uId);
	}
	std::map<mg_connection*, std::unique_ptr<P2PSession>>::iterator it1 = p2p_sessios_.begin();
	for (; it1 != p2p_sessios_.end(); it1++)
	{
		if (it1->first != con)
		{
			vec.push_back(it1->second->m_uId);
		}
	}
	return vec.size();
}
bool P2PServer::TimerFun()
{

	//std::unique_lock<std::mutex> lock(mutex_);
	std::map<mg_connection*, std::unique_ptr<P2PSession>>::iterator it1 = p2p_sessios_.begin();

	m_count++;
	int index = m_count % 10;

	char name[128];

	sprintf(name, "./img/%d.jpg", index);

	FILE* fp = fopen(name, "rb");

	printf("######### timerfun %d \n", index);

	int ret1 = 0;
	std::vector<char> mbuffer;
	mbuffer.resize(1024 * 1024);
	if (fp)
	{
		ret1 = fread(mbuffer.data(), 1, 1024 * 1024, fp);

	}
	if (fp)
	{
		fclose(fp);
		fp = NULL;
	}

	for (; it1 != p2p_sessios_.end(); it1++)
	{
		if (ret1 > 0)
		{
			mg_ws_send(it1->first, mbuffer.data(), ret1, WEBSOCKET_OP_BINARY);
		}
	}

	return true;
}
void P2PServer::DoWsMeg(struct mg_connection* conn, void* ev_data, void* user_data)
{
	char buf[1500];
	//std::unique_lock<std::mutex> lock(mutex_);
	struct mg_ws_message* wm = (struct mg_ws_message*)ev_data;
	std::map<mg_connection*, std::unique_ptr<P2PSession>>::iterator iter = p2p_sessios_.find(conn);
	if (iter != p2p_sessios_.end()) {
		//iter->second->OnRead(std::string(wm->data.ptr, wm->data.len));
		uint16_t* p = (uint16_t*)wm->data.buf;

		if (100 == p[0])
		{
			// 这里需要账号去重，如果已经存在去除之前的
			uint16_t id = p[1];
			bool isexit = IsExistence(id);
			if (isexit)
			{
				bool ret = DelAccount(id);
				printf("DelAccount ret :%d \n", ret);
			}
			iter->second->m_uId = id;
			uint16_t* p = (uint16_t*)buf;
			p[0] = 100;
			p[1] = 0;
			p[2] = 0;
			p[3] = 0;
			mg_ws_send(conn, buf, 8, WEBSOCKET_OP_TEXT);

		}
		// 在线列表
		else if (102 == p[0])
		{
			std::vector<uint16_t> tVec;
			GetListOnLineArray(conn, tVec);
			uint16_t* p = (uint16_t*)buf;
			p[0] = 102;
			p[1] = tVec.size();
			memcpy(&p[2], &tVec[0], tVec.size() * 2);

			mg_ws_send(conn, buf, tVec.size() * 2 + 2 + 2, WEBSOCKET_OP_TEXT);
		}
		else
		{
			printf("wm->data.ptr:%s \n", wm->data.buf);
			std::string  re = "websocket ret \n";
			std::string re1 = wm->data.buf;
			std::string ret = re + re1;
#if 0
			if (m_TaskScheduler)
			{
				if (m_nTimerId <= 0) // 防止connect 多次，定时器添加多次
				{
					m_TaskScheduler->PostTask(std::bind(&P2PServer::TimerFun, this));

				}
			}
#endif

			//TimerFun();
			//for (int i = 0; i < 10; i++)
			//{
			//	this->TimerFun();
			//	std::this_thread::sleep_for(std::chrono::milliseconds(100));

			//}
			if (m_nTimerId < 0)
			{
				m_nTimerId = m_TaskScheduler->AddTimer(30, std::bind(&P2PServer::TimerFun, this));
			}
#if 0
			FILE* fp = fopen("123.jpg", "rb");

			std::vector<char> mbuffer;
			mbuffer.resize(1024 * 1024);
			if (fp)
			{
				int ret1 = fread(mbuffer.data(), 1, 1024 * 1024, fp);
				if (ret1 > 0)
				{
					mg_ws_send(conn, mbuffer.data(), ret1, WEBSOCKET_OP_BINARY);
				}
			}
			if (fp)
			{
				fclose(fp);
				fp = NULL;
			}
#endif
			//for (int i = 0; i < 10; i++)
			//{
			//	std::string temp = std::to_string(i);
			//	ret += temp;
			//	mg_ws_send(conn, ret.c_str(), strlen(ret.c_str()), WEBSOCKET_OP_TEXT);

			//	std::this_thread::sleep_for(std::chrono::milliseconds(1000));
			//}

		}
	}
	//mg_iobuf_delete(&conn->recv, conn->recv.len);
	printf("receiver :%s len:%d \n", std::string(wm->data.buf, wm->data.len).c_str(), wm->data.len);
	uint16_t* p = (uint16_t*)wm->data.buf;
	printf("receiver p0 1 2 3 :%d %d %d %d \n", p[0], p[1], p[2], p[3]);
}

void P2PServer::HandleHttpEvent(struct mg_connection* c, void* ev_data, void* user_data)
{
	printf("HandleHttpEvent \n");
	struct mg_http_message* hm = (struct mg_http_message*)ev_data;
	// 其他请求
	if (mg_match(hm->uri, mg_str("/api/hello"), NULL))
	{

		printf("HandleHttpEvent api/hello  \n");

		mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
		mg_http_printf_chunk(c, "ID PROTO TYPE      LOCAL           REMOTE\n");
		/*	for (struct mg_connection* t = connection->mgr->conns; t != NULL; t = t->next) {
				mg_http_printf_chunk(connection, "%-3lu %4s %s %M %M\n", t->id,
					t->is_udp ? "UDP" : "TCP",
					t->is_listening ? "LISTENING"
					: t->is_accepted ? "ACCEPTED "
					: "CONNECTED",
					mg_print_ip, &t->loc, mg_print_ip, &t->rem);
			}*/
		mg_http_printf_chunk(c, "");  // Don't forget the last empty chunk

		// --- 开启CORS
		/*mg_printf(connection, "HTTP/1.1 200 OK\r\n"
				  "Content-Type: text/plain\n"
				  "Cache-Control: no-cache\n"
				  "Content-Length: %d\n"
				  "Access-Control-Allow-Origin: *\n\n"
				  "%s\n", rsp.length(), rsp.c_str()); */
	}
	else if (mg_match(hm->uri, mg_str("/api/sum"), NULL))
	{
		// 简单post请求，加法运算测试
		char n1[100], n2[100];
		double result;
		/* Get form variables */
		mg_http_get_var(&hm->body, "n1", n1, sizeof(n1));
		mg_http_get_var(&hm->body, "n2", n2, sizeof(n2));

		/* Compute the result and send it back as a JSON object */
		result = strtod(n1, NULL) + strtod(n2, NULL);
		//mg_printf_http_chunk(connection, "{ \"result\": %s }", rsp.c_str());

		std::string ret = "{\"result\":" + std::to_string(result) + "}";
		mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
		mg_http_printf_chunk(c, "%s\n", ret.c_str());

		mg_http_printf_chunk(c, "");

	}
	else if (mg_match(hm->uri, mg_str("/api/f2/*"), NULL)) {
		mg_http_reply(c, 200, "", "{\"result\": \"%.*s\"}\n", (int)hm->uri.len,
			hm->uri);
	}
	else {
		//struct mg_http_serve_opts opts = { .root_dir = s_root_dir };
		//mg_http_serve_dir(c, ev_data, &opts);

		printf("HandleHttpEvent / \n");
		struct mg_http_serve_opts opts = { 0 };
		opts.root_dir = s_root_dir;
		//opts.ssi_pattern = s_ssi_pattern;
		mg_http_serve_dir(c, hm, &opts);
	}

	MG_INFO(("%.*s %.*s %lu -> %.*s %lu", hm->method.len, hm->method.buf,
		hm->uri.len, hm->uri.buf, hm->body.len, 3, c->send.buf + 9,
		c->send.len));

}
void P2PServer::OnEvent(struct mg_connection* conn, int ev, void* ev_data)
{

	P2PServer* server = reinterpret_cast<P2PServer*>(conn->fn_data);
	if (!server) {
		return;
	}

	//std::lock_guard<std::mutex> locker(server->mutex_);
	//std::unique_lock<std::mutex> lock(server->mutex_);
	if (ev == MG_EV_WS_MSG) {
		// Got websocket frame.
		server->DoWsMeg(conn, ev_data, conn->fn_data);

	}
	else if (ev == MG_EV_WS_OPEN)
	{
		printf("MG_EV_WS_OPEN #### \n");
	}
	else if (ev == MG_EV_HTTP_MSG) {
		struct mg_http_message* hm = (struct mg_http_message*)ev_data;
		if (mg_match(hm->uri, mg_str("/websocket"), NULL)) {
			// Upgrade to websocket
			mg_ws_upgrade(conn, hm, NULL);

			if (server->p2p_sessios_.find(conn) == server->p2p_sessios_.end()) {
				//webrtc::PeerConnectionFactoryInterface* peer_connection_factory = server->peer_connection_factory_.get();
				//rtc::scoped_refptr<P2PSession> session(new rtc::RefCountedObject<P2PSession>(peer_connection_factory));
				//session->SetWriteCallback([conn](std::string message) {
				//	mg_ws_send(conn, message.c_str(), message.size(), WEBSOCKET_OP_TEXT);
				//});
				////session->AddAudioSource(server->audio_source_);
				//session->AddVideoSource(server->video_source_);

				std::unique_ptr<P2PSession> session(new P2PSession());// = std::make_unique<P2PSession>();
				server->p2p_sessios_[conn] = std::move(session);
				printf("add connected ... \n");
			}
		}
		else
		{
			server->HandleHttpEvent(conn, ev_data, conn->fn_data);
		}
	}
	else if (ev == MG_EV_ACCEPT) {

	}
	else if (ev == MG_EV_CLOSE) {
		server->p2p_sessios_.erase(conn);
		printf("MG_EV_CLOSE connected ... \n");
	}
}
